// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: Unified Transport Abstraction Layer Unified Transport Abstraction Layer
// ------------------------------------------------------------------

#ifndef SRC_CPP_RTPS_TRANSPORT_TCPCHANNELRESOURCE_H_
#define SRC_CPP_RTPS_TRANSPORT_TCPCHANNELRESOURCE_H_

#include <transport/TCPTransportDescriptor.h>
#include <transport/ReceiverResource.h>
#include <ertps/utils/LocatorAlias.h>
#include <transport/ChannelResource.h>
#include <asio.hpp>
#include <edds/rtps/common/Types.h>
using namespace evbs;
namespace vbs {
namespace transport {
// Typedefs
typedef std::shared_ptr<asio::ip::udp::socket> vbsUDPSocket;
typedef vbsUDPSocket vbsUDPSocketRef;

class TCPConnector;
class TCPTransportInterface;

enum eSocketErrorCodes { eNoError, eBrokenPipe, eAsioError, eSystemError, eException, eConnectionAborted = 125 };

class TCPChannelResource : public ChannelResource {
 protected:
    enum TCPConnectionType { TCP_ACCEPT_TYPE = 0, TCP_CONNECT_TYPE = 1 };

    enum eConnectionStatus {
        eDisconnected = 0,
        eConnecting,              // Output -> Trying connection.
        eConnected,               // Output -> Send bind message.
        eWaitingForBind,          // Input -> Waiting for the bind message.
        eWaitingForBindResponse,  // Output -> Waiting for the bind response message.
        eEstablished,
        eUnbinding
    };

    TCPTransportInterface* parent_;
    Locator locator_;
    Locator remote_locator_;
    std::mutex read_mutex_;
    std::atomic<eConnectionStatus> connection_status_;

 public:
    void disable() override;

    bool connection_established() { return connection_status_ == eConnectionStatus::eEstablished; }

    eConnectionStatus connection_status() { return connection_status_; }

    inline const Locator& locator() const { return locator_; }
    inline const Locator& remote_locator() const { return remote_locator_; }

    // Socket related methods
    virtual bool connect(const std::shared_ptr<TCPChannelResource>& myself) = 0;

    virtual void disconnect() = 0;

    virtual uint32_t read(ertps::rtps::octet* buffer, std::size_t size, asio::error_code& ec) = 0;

    virtual size_t send(const ertps::rtps::octet* header, size_t header_size, const ertps::rtps::octet* buffer,
                        size_t size, asio::error_code& ec) = 0;

    virtual void async_send(const ertps::rtps::octet* header, size_t header_size, const ertps::rtps::octet* buffer,
                            size_t size, AsyncSendHandler handler, bool is_default_header = false) = 0;

    virtual asio::ip::tcp::endpoint remote_endpoint() const = 0;

    virtual asio::ip::tcp::endpoint local_endpoint() const = 0;

    virtual void set_options(const TCPTransportDescriptor* options) = 0;

    virtual void cancel() = 0;

    virtual void close() = 0;

    virtual void shutdown(asio::socket_base::shutdown_type what) = 0;

    TCPConnectionType tcp_connection_type() const { return tcp_connection_type_; }

 protected:
    // Constructor called when trying to connect to a remote server
    TCPChannelResource(TCPTransportInterface* parent, const Locator& locator, const Locator& remote_locator,
                       uint32_t maxMsgSize);
    TCPChannelResource(TCPTransportInterface* parent, const Locator& locator, uint32_t maxMsgSize);
    // Constructor called when local server accepted connection
    TCPChannelResource(TCPTransportInterface* parent, uint32_t maxMsgSize);
    TCPChannelResource(TCPTransportInterface* parent, vbsUDPSocket& socket, uint32_t maxMsgSize,
                       const vbsutil::xmlparser::Locator_t& locator, std::shared_ptr<ReceiverResource> receiver);
    inline eConnectionStatus change_status(eConnectionStatus s) {
        eConnectionStatus old = connection_status_.exchange(s);
        return old;
    }

    TCPConnectionType tcp_connection_type_;

    friend class TCPTransportInterface;

    inline ReceiverResource* message_receiver() { return message_receiver_.get(); }

 private:
    std::shared_ptr<ReceiverResource> message_receiver_;

    TCPChannelResource(const TCPChannelResource&) = delete;

    TCPChannelResource& operator=(const TCPChannelResource&) = delete;
};

}  // namespace transport
}  // namespace vbs

#endif  // SRC_CPP_RTPS_TRANSPORT_TCPCHANNELRESOURCE_H_
